package com.snpic.appaw.module.insure.service.vehicleInformation;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.amazonaws.services.s3.model.PutObjectResult;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.snpic.appaw.framework.common.constants.SystemConstant;
import com.snpic.appaw.framework.common.pojo.CommonResult;
import com.snpic.appaw.module.infra.api.config.ConfigApi;
import com.snpic.appaw.module.insure.config.S3Config;
import com.snpic.appaw.module.insure.controller.app.cxProvince.vo.AppCxProvincePageReqVO;
import com.snpic.appaw.module.insure.controller.app.orc.vo.VehicleLicenseReqVO;
import com.snpic.appaw.module.insure.controller.app.orc.vo.VehicleLicenseRespVO;
import com.snpic.appaw.module.insure.dal.dataobject.customer.CustomerDO;
import com.snpic.appaw.module.insure.dal.dataobject.cxProvince.CxProvinceDO;
import com.snpic.appaw.module.insure.dal.mysql.customer.CustomerMapper;
import com.snpic.appaw.module.insure.enums.InsureConstant;
import com.snpic.appaw.module.insure.service.cxProvince.CxProvinceService;
import com.snpic.appaw.module.insure.service.orc.ClientOCRService;
import com.snpic.appaw.module.insure.service.s3.AmazonS3ObjectService;
import com.snpic.appaw.module.insure.util.s3.FileUtil;
import com.snpic.appaw.module.third.api.bjpt.BJPTApi;
import com.snpic.appaw.module.third.api.bjpt.dto.CarModelInfoInquiryReqDto;
import com.snpic.appaw.module.third.api.bjpt.dto.CarModelInfoInquiryReqTotalDto;
import com.snpic.appaw.module.third.api.bjpt.dto.CarModelInfoInquiryRespTotalDto;
import com.snpic.appaw.module.third.api.bjpt.dto.CxRequestHeadDto;
import com.snpic.appaw.module.third.api.commonCertification.CommonCertificationApi;
import com.snpic.appaw.module.third.api.commonCertification.dto.RealNameVerificationReqDTO;
import com.snpic.appaw.module.third.api.commonCertification.dto.RealNameVerificationRspDTO;
import com.snpic.appaw.module.third.enums.ThirdConstant;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import jakarta.annotation.Resource;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.snpic.appaw.module.insure.controller.app.vehicleInformation.vo.*;
import com.snpic.appaw.module.insure.dal.dataobject.vehicleInformation.VehicleInformationDO;
import com.snpic.appaw.framework.common.pojo.PageResult;
import com.snpic.appaw.framework.common.util.object.BeanUtils;

import com.snpic.appaw.module.insure.dal.mysql.vehicleInformation.VehicleInformationMapper;
import org.springframework.web.multipart.MultipartFile;

import static com.snpic.appaw.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.snpic.appaw.framework.common.util.collection.CollectionUtils.convertList;
import static com.snpic.appaw.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static com.snpic.appaw.module.insure.enums.ErrorCodeConstants.*;

/**
 * 首页车辆卡片/编辑家庭档案 - 车辆信息 Service 实现类
 *
 * @author jijun
 */
@Service
@Validated
@Slf4j
public class VehicleInformationServiceImpl implements VehicleInformationService {

	@Resource
	private ConfigApi configApi;
	@Resource
	private RedisTemplate<String, Integer> redisTemplate;

	@Resource
	private VehicleInformationMapper vehicleInformationMapper;
	@Resource
	private CustomerMapper customerMapper;

	@Resource
	private S3Config s3Config;

	@Resource
	private AmazonS3ObjectService amazonS3ObjectService;

	@Resource
	private FileUtil fileUtil;

	@Resource
	private CxProvinceService cxProvinceService;

	@Resource
	private CommonCertificationApi commonCertificationApi;

	@Resource
	private ClientOCRService clientOCRService;

		@Resource
	private BJPTApi bjptApi;

	/**
	 * 创建首页车辆卡片/编辑家庭档案 - 车辆信息
	 * 每位用户最多添加5辆车；当用户添加第6辆车时，阻断
	 *
	 * @param createReqVO 创建信息
	 * @return 编号
	 */
	@Override
	@Transactional
	public Long createVehicleInformation(AppVehicleInformationSaveReqVO createReqVO) {
		//1. 每位用户最多添加5辆车；当用户添加第6辆车时，阻断
		Long userId = getLoginUserId();
		Long count = vehicleExceedValid(userId);

		// 插入
		VehicleInformationDO vehicleInformation = BeanUtils.toBean(createReqVO, VehicleInformationDO.class);
		vehicleInformation.setUserId(userId);
		vehicleInformation.setIsAuthentication("N");  //未认证

		if (createReqVO.getOwnerStatus()) {
			//2.1 如果是车主，就要去填充车主信息，调用这个接口时，用户必然是实名的。
			CustomerDO customerDO = customerMapper.selectById(userId);
			if (customerDO == null || !SystemConstant.StrOne.equals(customerDO.getIdAuth()) || StrUtil.isEmpty(customerDO.getIdType()) || StrUtil.isEmpty(customerDO.getIdNo())) {
				log.error("当前登录用户未实名,customerDO = {}", customerDO);
				throw exception(LOGIN_USER_NOT_VERIFIED);
			}
			vehicleInformation.setIdType(customerDO.getIdType());
			vehicleInformation.setIdNo(customerDO.getIdNo());
			vehicleInformation.setCarOwner(customerDO.getRealName());
		} else {
			//2.2 非主车,在前端会手工输入车主姓名和证件号,需要进行二要素校验
			//2.2.1 只有使用身份证的车主才能进行二要素校验
			if (InsureConstant.ID_TYPE.equals(createReqVO.getIdType())) {
				boolean verified = twoFactorVerification(createReqVO.getIdNo(), createReqVO.getCarOwner(), userId);
				if (!verified) {
					log.error("身份证{}和车主{}未通过二要素校验", createReqVO.getIdNo(), createReqVO.getCarOwner());
					throw exception(OWNER_AND_ID_MISMATCH);
				}
			}
		}
		//3.第一辆车就是选中状态
		if (count == 0) {
			vehicleInformation.setSelectStatus(true);
			vehicleInformation.setSelectStatusForQuote(true);
		}
		vehicleInformationMapper.insert(vehicleInformation);
		// 返回
		return vehicleInformation.getId();
	}

	/**
	 * 二要素校验.调用统一配置化实名验真, 各渠道场景实名验真
	 *
	 * @param IdNo     身份证号码
	 * @param CarOwner 车主姓名
	 * @return
	 */
	private boolean twoFactorVerification(String IdNo, String CarOwner, Long userId) {

		boolean result = false;
		//1. commonCertificationApi.realNameVerification(realNameVerificationReqDTO),每日每人最多调用20次
		String dailyKey = "realNameVerify:" + userId + ":" + DateUtil.format(new Date(), "yyyyMMdd");
		Integer count = redisTemplate.opsForValue().get(dailyKey);
		count = ObjectUtil.isNotNull(count) ? count : SystemConstant.Zero;
		int maxCount = Integer.parseInt(configApi.getConfigValue(InsureConstant.CommonCertificationLimit));
		if (count >= maxCount) {
			log.error("用户[{}]当日实名验证(二要素)次数已达上限20次", userId);
			throw exception(OWNER_AND_ID_VERIFICATION_EXCEEDED);
		}


		//2.  调用统一配置化实名验真, 各渠道场景实名验真,传入姓名和身份证号码校验,即二要素校验
		RealNameVerificationReqDTO realNameVerificationReqDTO = new RealNameVerificationReqDTO();
		realNameVerificationReqDTO.setIdNo(IdNo);
		realNameVerificationReqDTO.setName(CarOwner);
		CommonResult<RealNameVerificationRspDTO> commonResult = null;


		try {
			commonResult = commonCertificationApi.realNameVerification(realNameVerificationReqDTO);
		} catch (Exception e) {
			log.error("调用统一配置化实名验真, 各渠道场景实名验真,接口调用异常,入参realNameVerificationReqDTO={}", realNameVerificationReqDTO, e);
			throw exception(THIRD_PARTY_INTERFACE_CALL_FAILED);
		} finally {
			if (count == null || count == 0) {
				redisTemplate.opsForValue().set(dailyKey, SystemConstant.One, SystemConstant.Two, TimeUnit.DAYS);
			} else {
				redisTemplate.opsForValue().increment(dailyKey);
			}
		}
		if (commonResult.isError()) {
			log.error("二要素校验失败, commonResult = {}", commonResult);
			throw exception(TWO_FACTOR_VERIFICATION_FAILED);
		}
		RealNameVerificationRspDTO realNameVerificationRspDTO = commonResult.getData();
		if (!InsureConstant.REAL_NAME_VERIFICATION_SUCCESS.equals(realNameVerificationRspDTO.getStatusCode())) {
			if (InsureConstant.REAL_NAME_VERIFICATION_EXCEED.equals(realNameVerificationRspDTO.getStatusCode())) {
				log.error("调用统一配置化实名验真, 各渠道场景实名验真,实名认证调用次数超每日上限！");
				throw exception(TWO_FACTOR_VERIFICATION_FAILED);
			}
			log.error("二要素校验失败, realNameVerificationRspDTO = {}", realNameVerificationRspDTO);
			throw exception(TWO_FACTOR_VERIFICATION_FAILED);
		}
		//身份证和姓名不匹配,二要素校验返回校验失败.
		RealNameVerificationRspDTO.Returns realNameVerificationRspDTOReturns = realNameVerificationRspDTO.getReturns();
		if (SystemConstant.StrOne.equals(realNameVerificationRspDTOReturns.getResult())) {
			log.info("身份证{}和姓名{}通过二要素校验.realNameVerificationRspDTOReturns={}", IdNo, CarOwner, realNameVerificationRspDTOReturns);
			return true;
		} else if (SystemConstant.StrTwo.equals(realNameVerificationRspDTOReturns.getResult())) {
			log.warn("身份证{}和姓名{}未通过二要素校验.realNameVerificationRspDTOReturns={}", IdNo, CarOwner, realNameVerificationRspDTOReturns);
		} else if (SystemConstant.StrThree.equals(realNameVerificationRspDTOReturns.getResult())) {
			log.warn("身份证{}和姓名{}二要素校验,未找到匹配数据.realNameVerificationRspDTOReturns={}", IdNo, CarOwner, realNameVerificationRspDTOReturns);
		} else {
			log.warn("身份证{}和姓名{}二要素校验中,有其他错误,realNameVerificationRspDTOReturns={}", IdNo, CarOwner, realNameVerificationRspDTOReturns);
		}
		return result;
	}

	/**
	 * 校验userId名下的车辆超过5辆
	 *
	 * @param userId
	 */
	private Long vehicleExceedValid(Long userId) {
		Long count = vehicleInformationMapper.selectCount(
			Wrappers.lambdaQuery(VehicleInformationDO.class)
				.eq(VehicleInformationDO::getUserId, userId) // 按用户ID过滤
		);
		if (count >= 5) {
			log.error("您名下的车辆数量已超过5辆，如需添加新车，请先删除不常用的车辆");
			throw exception(VEHICLE_EXCEED_LIMIT);
		}
		return count;
	}

	/**
	 * 首页车辆卡片/编辑家庭档案 - 更新车辆信息
	 *
	 * @param updateReqVO 更新信息
	 */
	@Override
	public VehicleLicenseRespVO updateVehicleInformation(MultipartFile vehicleLicenseFile, AppVehicleInformationSaveReqVO updateReqVO) {
		if (updateReqVO.getId() == null) {
			log.error("必须使用id更新vehicleInformationDO");
			throw exception(UPDATE_MUST_BY_ID);
		}
		Long VehicleInformationSaveId = updateReqVO.getId();
		String bucketName = s3Config.getBucketName();
		Long userId = getLoginUserId();
		VehicleInformationDO updateObj = BeanUtils.toBean(updateReqVO, VehicleInformationDO.class);

		// 2 传入行驶证就要进行如下的操作,ORC,存入S3
		VehicleLicenseRespVO vehicleLicenseRespVO = null;
		if (vehicleLicenseFile != null && !vehicleLicenseFile.isEmpty()) {
			//2.1 ORC识别
			//2.1.1 图片对象转成base64
			// 将 MultipartFile 转换为 Base64
			byte[] fileBytes = null;
			try {
				fileBytes = vehicleLicenseFile.getBytes();
			} catch (IOException e) {
				log.error("图片转base64出错", e);
				throw exception(IMAGE_TO_BASE64_ERROR);
			}
			String base64Image = Base64.getEncoder().encodeToString(fileBytes);

			//2.1.2 准备ORC的入参
			VehicleInformationDO vehicleInformationDO = vehicleInformationMapper.selectById(VehicleInformationSaveId);
			VehicleLicenseReqVO reqVO = new VehicleLicenseReqVO()
				.setImage_base64(base64Image)
				.setCarOwer(vehicleInformationDO.getCarOwner())
				.setLicensePlateNumber(vehicleInformationDO.getLicensePlateNumber());
			if (SystemConstant.StrOne.equals(vehicleInformationDO.getIdType())) {
				reqVO.setIdNo(vehicleInformationDO.getIdNo());
			}

			//2.1.3 调用OCR服务
			vehicleLicenseRespVO = clientOCRService.vehicleLicense(reqVO);

			//2.2 vehicleLicenseFile!=null就把它存入S3
			String uniqueFilename = fileUtil.getUniqueFilename(vehicleLicenseFile, InsureConstant.S3_DIR_VEHICLE_LICENSE);
			try {
				PutObjectResult putObjectResult = amazonS3ObjectService.uploadMultipartFileToS3(bucketName, uniqueFilename, vehicleLicenseFile);
			} catch (Exception e) {
				log.error("S3存储文件失败", e);
				throw exception(S3_FILE_STORAGE_FAILED);
			}
			updateObj.setVehicleLisenceUrl(uniqueFilename);
		}

		//3. 如果是isAuthentication="Y",需要考虑车辆选中状态.
		if ("Y".equals(updateReqVO.getIsAuthentication())) {
			List<VehicleInformationDO> vehicleInformationList = getVehicleInformationList(new AppVehicleInformationPageReqVO().setIsAuthentication("Y"));
			if (vehicleInformationList.size() == SystemConstant.One) {
				//如果只有一辆车,且认证状态为Y,则需要把该车置为选中状态.
				updateReqVO.setSelectStatus(true);
			}
		}

		// 校验存在
//		validateVehicleInformationExists(updateReqVO.getId());

		// 更新
		vehicleInformationMapper.updateById(updateObj);
		return vehicleLicenseRespVO;
	}

	/**
	 * 首页车辆卡片/编辑家庭档案 - 删除车辆信息
	 *
	 * @param id 编号
	 */
	@Override
	public void deleteVehicleInformation(Long id) {
		// 校验存在
		validateVehicleInformationExists(id);
		// 删除
		vehicleInformationMapper.deleteById(id);
	}

	/**
	 * 首页车辆卡片/编辑家庭档案 - 批量删除车辆信息
	 *
	 * @param ids 编号
	 */
	@Override
	public void deleteVehicleInformationListByIds(List<Long> ids) {
		// 删除
		vehicleInformationMapper.deleteByIds(ids);
	}


	private void validateVehicleInformationExists(Long id) {
		if (vehicleInformationMapper.selectById(id) == null) {
			throw exception(VEHICLE_INFORMATION_NOT_EXISTS);
		}
	}

	@Override
	public VehicleInformationDO getVehicleInformation(Long id) {

		return vehicleInformationMapper.selectById(id);
	}

	@Override
	public PageResult<VehicleInformationDO> getVehicleInformationPage(AppVehicleInformationPageReqVO pageReqVO) {
		return vehicleInformationMapper.selectPage(pageReqVO);
	}

	/**
	 * 首页车辆卡片/编辑家庭档案 - 获取车险用省城车牌分级列表
	 *
	 * @return
	 */
	@Override
	public ReginsListRespVO getReginsList() {

		PageResult<CxProvinceDO> cxProvincePage = cxProvinceService.getCxProvincePage(new AppCxProvincePageReqVO());

		// 按省份CxProvinceId分组
		Map<String, List<CxProvinceDO>> provinceMap = cxProvincePage.getList().stream()
			// 添加过滤条件：跳过 provinceId 为空的数据
			.filter(dto -> StrUtil.isNotEmpty(dto.getCxProvinceId()))
			.collect(Collectors.groupingBy(CxProvinceDO::getCxProvinceId));

		// 转换为前端需要的结构
		List<Province> provinceList = provinceMap.entrySet().stream()
			.map(entry -> {
				Province province = new Province();
				province.setProvinceId(entry.getKey());
				province.setProvinceName(entry.getValue().get(0).getCxProvinceName());

				// 提取城市列表
				List<City> cities = entry.getValue().stream()
					.map(dto -> new City(dto.getCxCityCode(), dto.getCxCityName(), dto.getCarPlate()))
//					.distinct()
					.collect(Collectors.toList());

				province.setCities(cities);
				return province;
			})
			.collect(Collectors.toList());

		ReginsListRespVO reginsListRespVO = new ReginsListRespVO();
		reginsListRespVO.setProvinceList(provinceList);
		return reginsListRespVO;
	}

	/**
	 * 首页车辆卡片/编辑家庭档案 - 当前用户绑定的车辆信息列表
	 *
	 * @return
	 */
	@Override
	public List<VehicleInformationDO> getVehicleInformationList(AppVehicleInformationPageReqVO pageReqVO) {
		//1. 补充userId
		if (pageReqVO.getUserId() == null) {
			pageReqVO.setUserId(getLoginUserId());
		}

		//2. 查询表vehicle_information
		List<VehicleInformationDO> vehicleInformationDOList = vehicleInformationMapper.selectList(pageReqVO);

		return vehicleInformationDOList;
	}

	/**
	 * 首页车辆卡片/编辑家庭档案 - 切换车辆的选中状态
	 * 含有两种状态,注意区分
	 * @param switchSelectStatusReqVO
	 * @return
	 */
	@Override
	@Transactional
	public boolean switchSelectStatus(SwitchSelectStatusReqVO switchSelectStatusReqVO) {
		Long vehicleInformationId = switchSelectStatusReqVO.getVehicleInformationId();
		Boolean selectStatus = switchSelectStatusReqVO.getSelectStatus();
		Boolean selectStatusForQuote = switchSelectStatusReqVO.getSelectStatusForQuote();
		boolean result = false;
		if (selectStatus!=null) {
			//1.1 先把所有车辆的选中状态改为false
			vehicleInformationMapper.update(
				Wrappers.lambdaUpdate(VehicleInformationDO.class)
					.set(VehicleInformationDO::getSelectStatus, false)
					.eq(VehicleInformationDO::getUserId, getLoginUserId())
			);
			//1.2. 再把当前车辆的选中状态改为目标状态true or false
			vehicleInformationMapper.update(
				Wrappers.lambdaUpdate(VehicleInformationDO.class)
					.set(VehicleInformationDO::getSelectStatus, selectStatus)
					.eq(VehicleInformationDO::getId, vehicleInformationId)
					.eq(VehicleInformationDO::getUserId, getLoginUserId())
			);
			result =  true;
		}
		if(selectStatusForQuote!=null){
			//2.1 先把所有车辆的选中状态改为false
			vehicleInformationMapper.update(
				Wrappers.lambdaUpdate(VehicleInformationDO.class)
					.set(VehicleInformationDO::getSelectStatusForQuote, false)
					.eq(VehicleInformationDO::getUserId, getLoginUserId())
			);
			//2.2 再把当前车辆的选中状态改为目标状态true or false
			vehicleInformationMapper.update(
				Wrappers.lambdaUpdate(VehicleInformationDO.class)
					.set(VehicleInformationDO::getSelectStatusForQuote, selectStatusForQuote)
					.eq(VehicleInformationDO::getId, vehicleInformationId)
					.eq(VehicleInformationDO::getUserId, getLoginUserId())
			);
			result = true;
		}
		return result;
	}

	/**
	 * 首页车辆卡片/编辑家庭档案 - 车辆车型查询接口,前端专用
	 *
	 * @param plateNo
	 * @param reqDTO
	 * @return
	 */
	@Override
	public CarModelInfoInquiryRespTotalDto carModelInfoInquiry(String plateNo, CarModelInfoInquiryReqTotalDto reqDTO) {
		CxProvinceDO cxProvinceDO = clientOCRService.getCxProvinceDO(plateNo);
		String companyCode = cxProvinceDO.getCompanyCode();
		String salesmanCode = cxProvinceDO.getSalesmanCode();
		CxRequestHeadDto cxRequestHeadDto = reqDTO.getRequestHead();
		//虚拟业务员
		cxRequestHeadDto.setComCode(companyCode);
		cxRequestHeadDto.setUserCode(salesmanCode);

		cxRequestHeadDto.setClientSeqNo(UUID.randomUUID().toString());
		CarModelInfoInquiryReqDto requestBody = reqDTO.getRequestBody();
		requestBody.setCompanyCode(companyCode);
		CommonResult<CarModelInfoInquiryRespTotalDto> carModelInfoInquiryRespTotalDtoCommonResult = null;
		try {
			carModelInfoInquiryRespTotalDtoCommonResult = bjptApi.carModelInfoInquiry(reqDTO);
		} catch (Exception e) {
			log.error("bjptApi调用失败", e);
			throw exception(THIRD_PARTY_INTERFACE_CALL_FAILED);
		}
		if (carModelInfoInquiryRespTotalDtoCommonResult == null
				|| carModelInfoInquiryRespTotalDtoCommonResult.getCode() != 0
				|| carModelInfoInquiryRespTotalDtoCommonResult.getData() == null
				|| (!ThirdConstant.BJPT_SUCCESS_FLAG.equals(carModelInfoInquiryRespTotalDtoCommonResult.getData().getResponseHead().getResult())
					&& !ThirdConstant.BJPT_FAILURE_FLAG.equals(carModelInfoInquiryRespTotalDtoCommonResult.getData().getResponseHead().getResult()))
			) {
				log.error("调用报价平台的车辆车型查询接口失败,carModelInfoInquiryRespTotalDtoCommonResult={}", carModelInfoInquiryRespTotalDtoCommonResult);
				throw exception(THIRD_PARTY_INTERFACE_CALL_FAILED);
			}
		return carModelInfoInquiryRespTotalDtoCommonResult.getData();
	}

}